/*
 * Copyright 2011-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.mgmt.framework.json.util;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

/**
 * Jackson 工具类
 *
 * @author liym
 * @since 2019-01-18 15:11
 */
public final class JacksonUtils {

    private static final Logger logger = LoggerFactory.getLogger(JacksonUtils.class);

    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");

    /**
     * Jackson JSON 处理
     */
    private static final ObjectMapper OBJECT_MAPPER;
    /**
     * Jackson XML 处理
     *
     * @since 2019-01-18 12:35
     */
    private static final XmlMapper XML_MAPPER;

    static {
        JsonFactory factory = JsonFactory.builder()
                // 允许出现特殊字符和转义符
                .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
                // 允许出现单引号
                .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
                .build();

        OBJECT_MAPPER = new ObjectMapper(factory);

        // 设置日期格式
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat(DATE_TIME_PATTERN));
        // 设置为中国上海时区
        OBJECT_MAPPER.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        OBJECT_MAPPER.setLocale(Locale.CHINA);

        // 空值不序列化
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 空对象不要抛异常
        OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 反序列化时，属性不存在的兼容处理
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 单引号处理
        OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

        // 去掉默认的时间戳格式
        ///OBJECT_MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DATE_FORMATTER));
        module.addSerializer(LocalTime.class, new LocalTimeSerializer(DATE_FORMATTER));
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER));
        module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FORMATTER));
        module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FORMATTER));

        OBJECT_MAPPER.registerModule(module);

        // ===

        XML_MAPPER = new XmlMapper();

        // 空值不序列化
        XML_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // XML标签名：使用骆驼命名的属性名
        XML_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);

        // 设置转换模式
        XML_MAPPER.enable(MapperFeature.USE_STD_BEAN_NAMING);

        // 空对象不要抛异常
        XML_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 反序列化时，属性不存在的兼容处理
        XML_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        XML_MAPPER.registerModule(module);
    }

    private JacksonUtils() {
        throw new AssertionError("No instance!");
    }

    /**
     * 对象转 JSON
     *
     * @param obj 对象
     * @return JSON
     */
    public static String objToJson(Object obj) {
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            logger.error("objToJson error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

    /**
     * JSON 转对象
     *
     * @param jsonStr JSON
     * @param clazz 对象类型
     * @param <T> 对象类型
     * @return 对象
     */
    public static <T> T jsonToObj(String jsonStr, Class<T> clazz) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, clazz);
        } catch (IOException e) {
            logger.error("jsonToObj error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

    /**
     * JSON 转对象
     *
     * @param jsonStr JSON
     * @param javaType 对象类型
     * @param <T> 对象类型
     * @return 对象
     */
    public static <T> T jsonToObj(String jsonStr, JavaType javaType) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, javaType);
        } catch (IOException e) {
            logger.error("jsonToObj error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

    /**
     * JSON 转 Map
     *
     * @param jsonStr JSON
     * @return Map
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> jsonToMap(String jsonStr) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, Map.class);
        } catch (IOException e) {
            logger.error("jsonToMap error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

    /**
     * JSON 转 Map
     *
     * @param jsonStr JSON
     * @param valueType 值类型
     * @return {@code HashMap<String, T>}
     * @see com.fasterxml.jackson.databind.type.TypeFactory#constructMapType(Class, Class, Class)
     */
    public static <T> Map<String, T> jsonToMap(String jsonStr, Class<T> valueType) {
        return jsonToMap(jsonStr, String.class, valueType);
    }

    /**
     * JSON 转 Map
     *
     * @param jsonStr JSON
     * @param keyType 键类型
     * @param valueType 值类型
     * @return {@code HashMap<K, V>}
     * @see com.fasterxml.jackson.databind.type.TypeFactory#constructMapType(Class, Class, Class)
     * @since 2021-06-08 13:03
     */
    public static <K, V> Map<K, V> jsonToMap(String jsonStr, Class<K> keyType, Class<V> valueType) {
        try {
            MapType mapType = getTypeFactory().constructMapType(HashMap.class, keyType, valueType);
            return jsonToObj(jsonStr, mapType);
        } catch (Exception e) {
            logger.error("jsonToMap error", e);
            return Collections.emptyMap();
        }
    }

    /**
     * JSON 转 List
     *
     * @param jsonArrayStr JSON 数组
     * @param valueType 值类型
     * @param <T> 值类型
     * @return List
     * @see com.fasterxml.jackson.databind.type.TypeFactory#constructCollectionLikeType(Class, Class)
     * @see com.fasterxml.jackson.databind.type.TypeFactory#constructParametricType(Class, Class[])
     */
    public static <T> List<T> jsonToList(String jsonArrayStr, Class<T> valueType) {
        try {
            JavaType listType = getTypeFactory().constructParametricType(List.class, valueType);
            return jsonToObj(jsonArrayStr, listType);
        } catch (Exception e) {
            logger.error("jsonToList error", e);
            return Collections.emptyList();
        }
    }

    /**
     * Map 转对象
     *
     * @param map Map
     * @param clazz 对象类型
     * @param <T> 对象类型
     * @return 对象
     */
    public static <T> T mapToObj(Map<?, ?> map, Class<T> clazz) {
        return OBJECT_MAPPER.convertValue(map, clazz);
    }

    /**
     * @return {@link TypeFactory}
     * @since 2019-01-09 11:16
     */
    public static TypeFactory getTypeFactory() {
        return OBJECT_MAPPER.getTypeFactory();
    }

    /**
     * XML 转对象
     *
     * @param xmlStr XML
     * @param clazz 对象类型
     * @param <T> 对象类型
     * @return 对象
     * @since 2019-01-18 12:38
     */
    public static <T> T xmlToObj(String xmlStr, Class<T> clazz) {
        try {
            return XML_MAPPER.readValue(xmlStr, clazz);
        } catch (IOException e) {
            logger.error("xmlToObj error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

    /**
     * 对象转 XML
     *
     * @param obj 对象
     * @return XML
     * @since 2019-01-18 12:43
     */
    public static String objToXml(Object obj) {
        try {
            return XML_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            logger.error("objToXml error", e);
            throw new JsonParseException(e.getMessage());
        }
    }

}
